;;; firemacs-inspector.el --- Mode for inspecting the firefox DOM

;; Copyright 2006 Lukas Loehrer
;;
;; Author: loehrerl [at] gmx [dot] net
;; Version: $Id: mozremote-dom-inspector.el,v 0.0 2006/10/12 16:22:19 luke Exp $
;; Keywords: 
;; X-URL: not distributed yet

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; if not, write to the Free Software
;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

;;; Commentary:

;; 

;; Put this file into your load-path and the following into your ~/.emacs:
;;   (require 'firemacs-inspector)

;;; Code:

(provide 'firemacs-inspector)
(eval-when-compile
  (require 'cl))

(require 'outline)

(require 'mozremote)
(require 'mozremote-object)

(require 'firemacs-browser)
(require 'firemacs-utils)

(defvar fm-inspector-ref nil
  "Reference to the javascript Inspector object for this buffer.

This references a remote object.
This Variable is local to the current firemacs-inspector buffer.")

(define-derived-mode firemacs-inspector-mode fundamental-mode "firemacs-inspector"
  "Mode to inspect the Firefox DOM."
  (set (make-local-variable 'truncate-lines) t)
  (set (make-local-variable 'fm-inspector-ref) nil)
  (setq buffer-read-only t)
  (buffer-disable-undo)
  (outline-minor-mode t))

(let ((m firemacs-inspector-mode-map))
  (define-key m (kbd "RET") 'fm-inspector-inspect-at-point)
  (define-key m "r" 'fm-inspector-reset)
  (define-key m "u" 'fm-inspector-parent)
  (define-key m "p" 'fm-inspector-previous-sibling)
  (define-key m "n" 'fm-inspector-next-sibling)
  (define-key m "f" 'fm-inspector-first-child)
  (define-key m "q" 'fm-inspector-quit)
  (define-key m (kbd "SPC") 'show-entry))

(defun fm-inspector-buffer-create ()
  "Create a new buffer in firemacs-inspector mode."
  (with-current-buffer (generate-new-buffer "*firemacs-inspector*")
	(firemacs-inspector-mode)
	(current-buffer)))

(defun fm-inspector-format-dom-node (node &optional skip)
  (setq skip (append '(name index type) skip))
  (let ((node-name (mozremote-dictionary-get 'name node))
		(node-type (mozremote-dictionary-get 'type node)))
	(concat 
	 (format "%s type=%d" node-name node-type)
	 (let ((prop-list nil))
	   (mozremote-dictionary-do
		name1 value node
		(unless (memq name1 skip)
		  (push (fm-inspector-format-property name1 value ) prop-list)))
	   (apply 'concat prop-list)))))

(defun fm-inspector-format-property (name2 value)
  (cond
   ((stringp value) (format "%s=\"%s\"" name2 value))
   ((numberp value) (format "%s=%f" name2 value))
   (t (format "%s=%s" name2 value))))

(defun fm-inspector-dom-display (info)
  "Display info in the current inspector buffer."
  (let ((em (mozremote-dictionary-get 'errorMessage info)))
	(if em
		(error em)))
  (let ((inhibit-read-only t))
	(erase-buffer)
	(insert (format "* %s\n\n" (fm-inspector-format-dom-node info '(childNodes attributes))))
	(fm-inspector-dom-display-attributes (mozremote-dictionary-get 'attributes info))
	(insert "\n")
	(fm-inspector-dom-display-children (mozremote-dictionary-get 'childNodes info))
	(hide-body)
	(goto-char (point-min))
	(emacspeak-speak-line)))


(defun fm-inspector-dom-display-attributes (attributes)
  (fm-insert-line-with-props "* Node attributes:"
							 '(fm-inspector-part-type outline-heading))
  (insert (mapconcat 'identity
					 (let ((attrs nil))
					   (mozremote-dictionary-do name1 value attributes
															(push (format "%s=\"%s\"" name1 value) attrs))
					   attrs) "\n")))

(defun fm-inspector-dom-display-children (children)
  (fm-insert-line-with-props "* Child nodes:"
							 '(fm-inspector-part-type outline-heading))
  (if children
	  (dolist (child children)
		(let (			(node-index (mozremote-dictionary-get 'index child)))
		  (fm-insert-line-with-props
		   (fm-inspector-format-dom-node child)
		   `(fm-inspector-part-type child-node fm-inspector-child-index ,node-index))))
		  (insert "No child nodes.\n")))


(defun fm-inspector-refresh ()
  (interactive)
  (unless (eq major-mode 'firemacs-inspector-mode)
	(error "Not in a firemacs inspector buffer."))
  (let ((info (mozremote-object-call-method-sync fm-inspector-ref "getDOMNodeInfo"))) 
	(fm-inspector-dom-display info)))


(defun fm-inspector-for-inspector (insp)
  "Creates new firemacs-inspector buffor for inspector insp and switches to it."
  (switch-to-buffer
   (with-current-buffer (fm-inspector-buffer-create)
	 (setq fm-inspector-ref insp)
	 (fm-inspector-refresh)
	 (current-buffer))))



(defun fm-inspector-for-url (url)
  (interactive "sURL: ")
  (firemacs-browser-load-url-in-new-window 
   (lambda (win)
	 (fm-inspector-for-inspector (mozremote-object-call-method-sync win "createInspector")))
   url))
  
;;{{{Interactive commands


(defun fm-inspector-reset ()
  (interactive)
  (let ((info (mozremote-object-call-method-sync fm-inspector-ref "reset")))
	(fm-inspector-dom-display info)))

(defun fm-inspector-first-child ()
  (interactive)
  (let ((info (mozremote-object-call-method-sync fm-inspector-ref "domNthChild" 0)))
	(fm-inspector-dom-display info)))


(defun fm-inspector-next-sibling ()
  (interactive)
  (let ((info (mozremote-object-call-method-sync fm-inspector-ref "domNextSibling")))
	(fm-inspector-dom-display info)))

(defun fm-inspector-previous-sibling ()
  (interactive)
  (let ((info (mozremote-object-call-method-sync fm-inspector-ref "domPreviousSibling")))
	(fm-inspector-dom-display info)))

(defun fm-inspector-parent ()
  (interactive)
  (let ((info (mozremote-object-call-method-sync fm-inspector-ref "domParent")))
	(fm-inspector-dom-display info)))
  

(defun fm-inspector-inspect-at-point (pos)
  "Inspects the object  around point."
  (interactive "d")
  (case (get-text-property pos 'fm-inspector-part-type)
	('child-node 
	 (let ((index (get-text-property pos 'fm-inspector-child-index)))
	   (if index
		   (let ((info (mozremote-object-call-method-sync   fm-inspector-ref "domNthChild" index)))
			 (fm-inspector-dom-display info)))))
	('outline-heading
	 (show-entry))
	(nil (message "No known object at point."))))
	
	 
  


;;}}}





;;{{{  emacs local variables

;;; local variables:
;;; folded-file: t
;;; byte-compile-dynamic: t
;;; end:

;;}}}

;;; firemacs-inspector.el ends here
